package com.inoneone.youba.mcht.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.inoneone.toolkit.SnowFlake;
import com.inoneone.toolkit.enums.BooleanType;
import com.inoneone.toolkit.enums.EventType;
import com.inoneone.toolkit.enums.OrderStatus;
import com.inoneone.toolkit.exception.AssertUtil;
import com.inoneone.toolkit.exception.BusinessRuntimeException;
import com.inoneone.toolkit.exception.RegExp;
import com.inoneone.youba.mcht.cache.mapper.RiskMapper;
import com.inoneone.youba.mcht.cache.po.RiskPo;
import com.inoneone.youba.mcht.conf.ValueConfig;
import com.inoneone.youba.mcht.dao.mapper.*;
import com.inoneone.youba.mcht.dao.po.*;
import com.inoneone.youba.mcht.dto.PageRsp;
import com.inoneone.youba.mcht.dto.req.*;
import com.inoneone.youba.mcht.dto.rsp.Data7DayRsp;
import com.inoneone.youba.mcht.dto.rsp.FuelOrderRsp;
import com.inoneone.youba.mcht.enums.Constants;
import com.inoneone.youba.mcht.enums.ErrorCode;
import com.inoneone.youba.mcht.pojo.Event;
import com.inoneone.youba.mcht.service.CardApi;
import com.inoneone.youba.mcht.service.MsgCenterApi;
import com.inoneone.youba.mcht.service.OrderApi;
import com.inoneone.youba.mcht.service.SafeApi;
import com.inoneone.youba.mcht.util.CommonUtil;
import com.inoneone.youba.mcht.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


@Service
public class OrderApiImpl implements OrderApi {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ExtendOrderMapper extendOrderMapper;

    @Autowired
    private FuelOrderMapper fuelOrderMapper;

    @Autowired
    private BusinessLockMapper businessLockMapper;

    @Autowired
    private FuelOrderFlowMapper fuelOrderFlowMapper;

    @Autowired
    private TankerMapper tankerMapper;

    @Autowired
    MsgCenterApi msgCenterApi;

    @Autowired
    private CardApi cardApi;

    @Autowired
    SafeApi safeApi;

    @Autowired
    RiskMapper riskMapper;

    @Autowired
    ValueConfig valueConfig;

    @Autowired
    SnowFlake snowFlake;

    /**
     * Description: 查询最近七天加油、资金数据</br>
     * Date: 2017-06-21 09:24:34</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public Data7DayRsp countByMch(String mchId) {

        //成功状态
        List<Integer> orderStatus = new ArrayList<Integer>();
        orderStatus.add(OrderStatus.SUCCESS.getCode());

        //开始时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date startDate = calendar.getTime();

        //结束时间
        calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + 7);
        Date endDate = calendar.getTime();

        FuelOrderExample orderExample = new FuelOrderExample();
        orderExample.createCriteria().andMchIdEqualTo(mchId)
                .andStatusIn(orderStatus).andEndTimeBetween(startDate, endDate);
        List<CountByMchRsp> countByMchRsps = extendOrderMapper.countByMch(orderExample);

        //返回数据
        Data7DayRsp data7DayRsp = new Data7DayRsp();
//        data7DayRsp.setOrderDate(DateUtil.formatDate(countByMchRsp.getEndTime(), "yyyy-MM-dd"));
//        data7DayRsp.setTotalAmount(NumUtil.decimalFormat(countByMchRsp.getTotalAmount()));
//        data7DayRsp.setTotalCount(String.valueOf(countByMchRsp.getTotalount()));
//        data7DayRsp.setTotalFuel(NumUtil.decimalFormat(countByMchRsp.getTotalFuel()));
        return data7DayRsp;
    }

    /**
     * Description: 查询加油订单</br>
     * Date: 2017-06-23 15:30:58</br>
     *
     * @param fuelOrderReq @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public PageRsp<FuelOrderRsp> fuelOrder(FuelOrderReq fuelOrderReq, String mchId) {

        //查询条件
        FuelOrderExample orderExample = new FuelOrderExample();
        orderExample.setOrderByClause("create_time desc");
        FuelOrderExample.Criteria criteria = orderExample.createCriteria();
        criteria.andMchIdEqualTo(mchId);
        criteria.andStatusEqualTo(OrderStatus.SUCCESS.getCode());
        if (!CommonUtil.isNullOrEmpty(fuelOrderReq.getCardId())) {
            criteria.andCardIdEqualTo(fuelOrderReq.getCardId());
        }
        if (!CommonUtil.isNullOrEmpty(fuelOrderReq.getTankerId())) {
            criteria.andTankerIdEqualTo(fuelOrderReq.getTankerId());
        }

        if (fuelOrderReq.getBeginTime() != null) {
            criteria.andCreateTimeGreaterThanOrEqualTo(new Date(fuelOrderReq.getBeginTime()));
        }
        if (fuelOrderReq.getEndTime() != null) {
            criteria.andCreateTimeLessThanOrEqualTo(new Date(fuelOrderReq.getEndTime()));
        }

        //分页查询
        PageHelper.startPage(fuelOrderReq.getPageNum(), fuelOrderReq.getPageSize());
        List<FuelOrder> orders = fuelOrderMapper.selectByExample(orderExample);
        PageInfo<FuelOrder> pageCards = new PageInfo<>(orders);

        //转换输出类
        List<FuelOrderRsp> fuelOrderRspList = new ArrayList<>();
        for (FuelOrder order : orders) {
            FuelOrderRsp fuelOrderRsp = new FuelOrderRsp();
            BeanUtils.copyProperties(order, fuelOrderRsp);
            fuelOrderRspList.add(fuelOrderRsp);
        }
        PageRsp<FuelOrderRsp> pageFuelOrderRsp = new PageRsp<>(pageCards.getTotal(), pageCards.getPageSize(), pageCards.getPageNum(), fuelOrderRspList);

        return pageFuelOrderRsp;
    }

    /**
     * Description: 幂等查询业务锁，无数据则创建</br>
     * Date: 2017-07-05 11:28:06</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public BusinessLock getBusinessLock(String cardId) {
        BusinessLock businessLock = businessLockMapper.selectByPrimaryKey(cardId);
        if (businessLock == null) {
            businessLock = new BusinessLock();
            businessLock.setCardId(cardId);
            businessLock.setIsLocked(BooleanType.NO.getCode());
            businessLock.setCreateTime(new Date());
            businessLock.setModifyTime(businessLock.getCreateTime());
            int result = businessLockMapper.insertSelective(businessLock);
            if (result != 1) {
                throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
            }
        }
        return businessLock;
    }

    /**
     * Description: 业务锁加锁</br>
     * Date: 2017-07-05 13:34:54</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public void lock(String cardId, FuelOrder fuelOrder) {
        BusinessLock businessLock = new BusinessLock();
        businessLock.setModifyTime(new Date());
        businessLock.setCardId(cardId);
        businessLock.setIsLocked(BooleanType.YES.getCode());
        businessLock.setMchtId(fuelOrder.getMchId());
        businessLock.setOrderId(fuelOrder.getOrderId());
        businessLock.setTankId(fuelOrder.getTankerId());
        int result = businessLockMapper.updateByPrimaryKeySelective(businessLock);
        if (result != 1) {
            throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
        }
    }

    /**
     * Description: 业务锁解锁</br>
     * Date: 2017-07-05 16:51:27</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public void unlock(String cardId) {
        BusinessLock businessLock = new BusinessLock();
        businessLock.setModifyTime(new Date());
        businessLock.setCardId(cardId);
        businessLock.setIsLocked(BooleanType.NO.getCode());
        int result = businessLockMapper.updateByPrimaryKeySelective(businessLock);
        if (result != 1) {
            throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
        }

    }

    /**
     * Description: 新增订单</br>
     * Date: 2017-07-05 12:14:47</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public FuelOrder createOrder(Card card, Tanker tanker) {
        FuelOrder fuelOrder = new FuelOrder();
        fuelOrder.setOrderId(snowFlake.nextId());
        fuelOrder.setCardId(card.getCardId());
        fuelOrder.setCardName(card.getUserName());
        fuelOrder.setMchId(card.getMchId());
        fuelOrder.setMchName(card.getMchName());
        fuelOrder.setTankerId(String.valueOf(tanker.getTankerId()));
        fuelOrder.setTankerName(tanker.getName());
        fuelOrder.setCreateTime(new Date());
        fuelOrder.setModifyTime(fuelOrder.getCreateTime());
        fuelOrder.setPrice(tanker.getPrice());
        fuelOrder.setOilType(tanker.getOilType());
        fuelOrder.setModelType(tanker.getModelType());
        fuelOrder.setStatus(OrderStatus.WAIT.getCode());
        int result = fuelOrderMapper.insertSelective(fuelOrder);
        if (result != 1) {
            throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
        }

        return fuelOrder;
    }

    /**
     * Description: 根据订单号查询订单</br>
     * Date: 2017-07-05 15:28:36</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public FuelOrder queryOrder(Long orderId) {
        FuelOrder fuelOrder = fuelOrderMapper.selectByPrimaryKey(orderId);
        if (fuelOrder == null) {
            throw new BusinessRuntimeException(ErrorCode.ORDER_NOT_EXIST);
        }
        return fuelOrder;
    }

    /**
     * Description: 幂等完结上次订单</br>
     * Date: 2017-07-05 14:41:01</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Transactional
    @Override
    public void complateLastOrder(EndOrderReq endOrderReq, Merchant merchant, Card card, Tanker tanker) {

        //检查订单状态
        FuelOrder fuelOrder = queryOrder(Long.valueOf(endOrderReq.getOrderId()));
        if (fuelOrder.getStatus() != OrderStatus.WAIT.getCode()) {
            return;
        }


        //消费余额为0直接完成
        if (new BigDecimal(endOrderReq.getAmount()).compareTo(BigDecimal.ZERO) <= 0) {
            updateOrder(endOrderReq, OrderStatus.DOWN_NOTHING.getCode());
            return;
        } else {
            //nothing
        }

        //余额扣款
        RechargeReq rechargeReq = new RechargeReq();
        rechargeReq.setAmount(endOrderReq.getAmount());
        rechargeReq.setCardId(fuelOrder.getCardId());
        BigDecimal balance = cardApi.reduce(rechargeReq, fuelOrder.getMchId(), true);

        //修改加油机数据记录 TODO 需要select for update
        Tanker tankerUpdate  = new Tanker();
        BigDecimal chrageFuel = new BigDecimal(endOrderReq.getCharge());
        BigDecimal fuelAmount = new BigDecimal(endOrderReq.getAmount());
        tankerUpdate.setTankerId(tanker.getTankerId());
        tankerUpdate.setFuelCharge(tanker.getFuelCharge().add(chrageFuel));
        tankerUpdate.setRemainFuel(tanker.getRemainFuel().subtract(chrageFuel));
        tankerUpdate.setFuelCount(tanker.getFuelCount() + 1);
        tankerUpdate.setFuelAmount(tanker.getFuelAmount().add(fuelAmount));
        tankerMapper.updateByPrimaryKeySelective(tankerUpdate);

        //订单修改为完成状态
        updateOrder(endOrderReq, OrderStatus.SUCCESS.getCode());

        //余额不足提醒用户
        if (balance.subtract(valueConfig.getRiskLessBalance()).compareTo(BigDecimal.ZERO) <= 0)
        {
            logger.info("ic卡["+ card.getCardId() +"]余额不足提醒");
            //短信提醒
            if(CommonUtil.isNullOrEmpty(card.getMobile()))
            {
                msgCenterApi.sendBalanceLessSms(card.getMobile(), card.getCardId(), valueConfig.getRiskLessBalance().toString());
            }
            else
            {
                msgCenterApi.sendBalanceLessSmsToMch(card.getMobile(), card.getCardId(), valueConfig.getRiskLessBalance().toString());
            }
        }

        //加油消费金额过大风险提醒
        checkOverAmount(endOrderReq, merchant.getId(), merchant.getMchName(), merchant.getMobile(), card.getUserName(), fuelOrder.getCardId());


        //加油间隔频繁风险提醒
        checkInterval(endOrderReq, merchant.getId(), merchant.getMchName(), merchant.getMobile(), card.getUserName(), fuelOrder.getCardId());
    }

    /**
     * Description: 加油消费金额过大风险提醒</br>
     * Date: 2017-07-26 10:01:45</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
    */
    private void checkOverAmount(EndOrderReq endOrderReq, String mchId, String mchName, String mchMobile, String cardName, String cardId)
    {
        if (new BigDecimal(endOrderReq.getAmount()).compareTo(new BigDecimal(valueConfig.getRiskAmount())) >= 0) {
            logger.info("ic卡["+ cardId +"]消费金额过大风险提醒");
            //短信提醒
            msgCenterApi.sendRiskSms(mchMobile, cardId, DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"), endOrderReq.getAmount());

            //记录日志
            Event event = new Event();
            event.setType(EventType.RISK.getCode());
            event.setDesc("ic卡[" + cardId + "]执行了一笔" + endOrderReq.getAmount() + "元的大额加油消费");
            event.setMchId(mchId);
            event.setMchName(mchName);
            event.setCardId(cardId);
            event.setCardName(cardName);
            safeApi.log(event);
        }

    }

    /**
     * Description: 加油间隔频繁风险提醒</br>
     * Date: 2017-07-26 09:53:26</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
    */
    private void checkInterval(EndOrderReq endOrderReq, String mchId, String mchName, String mchMobile, String cardName, String cardId)
    {
        RiskPo riskPo = riskMapper.select(cardId);
        if(riskPo == null)
        {
            riskPo = new RiskPo();
            riskPo.setIcCard(cardId);
            riskPo.setCurrTime(new Date().getTime());
            riskPo = riskMapper.insertAndUpdate(riskPo);
        }
        else
        {
            long now = new Date().getTime();
            long intervalSec = (new Date().getTime() - riskPo.getCurrTime()) / 1000;
            if (intervalSec <= valueConfig.getRiskIntervalSec()) {
                logger.info("ic卡["+ cardId +"]加油频繁风险提醒");
                //短信提醒
                msgCenterApi.sendIntervalRiskSms(mchMobile, cardId, DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"), String.valueOf(intervalSec));

                //记录日志
                Event event = new Event();
                event.setType(EventType.RISK.getCode());
                event.setDesc("ic卡[" + cardId + "]执行了一笔加油消费，距离上次消费间隔" + intervalSec + "秒，消费过于频繁");
                event.setMchId(mchId);
                event.setMchName(mchName);
                event.setCardId(cardId);
                event.setCardName(cardName);
                safeApi.log(event);
            }

            riskPo.setCurrTime(now);
            riskMapper.insertAndUpdate(riskPo);
        }
    }

    /**
     * Description: 修改订单状态</br>
     * Date: 2017-07-25 16:27:43</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    private void updateOrder(EndOrderReq endOrderReq, int orderStatus) {
        FuelOrder fuelOrderUpdate = new FuelOrder();
        fuelOrderUpdate.setOrderId(Long.valueOf(endOrderReq.getOrderId()));
        fuelOrderUpdate.setEndTime(DateUtil.parseDate(endOrderReq.getEndTime() ,Constants.IOT_DATE_FORMAT));
        fuelOrderUpdate.setModifyTime(fuelOrderUpdate.getEndTime());
        fuelOrderUpdate.setStatus(orderStatus);
        if (orderStatus == OrderStatus.DOWN_NOTHING.getCode()) {
            fuelOrderUpdate.setAmount(BigDecimal.ZERO);
            fuelOrderUpdate.setFuelCharge(BigDecimal.ZERO);
        } else {
            fuelOrderUpdate.setAmount(new BigDecimal(endOrderReq.getAmount()));
            fuelOrderUpdate.setFuelCharge(new BigDecimal(endOrderReq.getCharge()));
        }

        int result = fuelOrderMapper.updateByPrimaryKeySelective(fuelOrderUpdate);
        if (result != 1) {
            throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
        }
    }


    /**
     * Description: 写加油订单流水</br>
     * Date: 2017-07-06 10:40:42</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @Override
    public void addOrderFlow(FuelOrder fuelOrder, AddSerialReq addSerialReq) {
        FuelOrderFlow fuelOrderFlow = new FuelOrderFlow();
        fuelOrderFlow.setSerialNo(Long.valueOf(addSerialReq.getSerialNo()));
        fuelOrderFlow.setOrderId(Long.valueOf(addSerialReq.getOrderId()));
        fuelOrderFlow.setCardId(fuelOrder.getCardId());
        fuelOrderFlow.setCardName(fuelOrder.getCardName());
        fuelOrderFlow.setAmount(new BigDecimal(addSerialReq.getAmount()));
        fuelOrderFlow.setFuelCharge(new BigDecimal(addSerialReq.getCharge()));
        fuelOrderFlow.setBalance(new BigDecimal(addSerialReq.getBalance()));
        fuelOrderFlow.setMchId(fuelOrder.getMchId());
        fuelOrderFlow.setMchName(fuelOrder.getMchName());
        fuelOrderFlow.setTankerId(fuelOrder.getTankerId());
        fuelOrderFlow.setTankerName(fuelOrder.getTankerName());
        fuelOrderFlow.setPrice(fuelOrder.getPrice());
        fuelOrderFlow.setOilType(fuelOrder.getOilType());
        fuelOrderFlow.setModelType(fuelOrder.getModelType());
        fuelOrderFlow.setCreateTime(DateUtil.parseDate(addSerialReq.getSerialTime(), Constants.IOT_DATE_FORMAT));
        int result = fuelOrderFlowMapper.insertSelective(fuelOrderFlow);
        if (result != 1) {
            throw new BusinessRuntimeException(ErrorCode.ERR_UPDATE);
        }
    }

}
